/*
 * Copyright 2012, Jérôme Duval, korli@users.berlios.de.
 * Copyright (c) 2003 Tyler Dauwalder, tyler@dauwalder.net
 * This file may be used under the terms of the MIT License.
 */


/*! \file UdfStructures.cpp

	UDF on-disk data structure definitions
*/

#include "UdfStructures.h"

#include <string.h>

#include "UdfString.h"
#include "Utils.h"


//----------------------------------------------------------------------
// Constants
//----------------------------------------------------------------------

const charspec kCs0CharacterSet(0, "OSTA Compressed Unicode");
//const charspec kCs0Charspec = { _character_set_type: 0,
//                                _character_set_info: "OSTA Compressed Unicode"
//                                                    "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0"
//                                                    "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0"
//                              };
                              
// Volume structure descriptor ids 
const char* kVSDID_BEA 			= "BEA01";
const char* kVSDID_TEA 			= "TEA01";
const char* kVSDID_BOOT 		= "BOOT2";
const char* kVSDID_ISO 			= "CD001";
const char* kVSDID_ECMA167_2 	= "NSR02";
const char* kVSDID_ECMA167_3 	= "NSR03";
const char* kVSDID_ECMA168		= "CDW02";

//! crc 010041 table, as generated by crc_table.cpp
const uint16 kCrcTable[256] = {
    0x0000, 0x1021, 0x2042, 0x3063, 0x4084, 0x50a5, 0x60c6, 0x70e7,
    0x8108, 0x9129, 0xa14a, 0xb16b, 0xc18c, 0xd1ad, 0xe1ce, 0xf1ef,
    0x1231, 0x0210, 0x3273, 0x2252, 0x52b5, 0x4294, 0x72f7, 0x62d6,
    0x9339, 0x8318, 0xb37b, 0xa35a, 0xd3bd, 0xc39c, 0xf3ff, 0xe3de,
    0x2462, 0x3443, 0x0420, 0x1401, 0x64e6, 0x74c7, 0x44a4, 0x5485,
    0xa56a, 0xb54b, 0x8528, 0x9509, 0xe5ee, 0xf5cf, 0xc5ac, 0xd58d,
    0x3653, 0x2672, 0x1611, 0x0630, 0x76d7, 0x66f6, 0x5695, 0x46b4,
    0xb75b, 0xa77a, 0x9719, 0x8738, 0xf7df, 0xe7fe, 0xd79d, 0xc7bc,
    0x48c4, 0x58e5, 0x6886, 0x78a7, 0x0840, 0x1861, 0x2802, 0x3823,
    0xc9cc, 0xd9ed, 0xe98e, 0xf9af, 0x8948, 0x9969, 0xa90a, 0xb92b,
    0x5af5, 0x4ad4, 0x7ab7, 0x6a96, 0x1a71, 0x0a50, 0x3a33, 0x2a12,
    0xdbfd, 0xcbdc, 0xfbbf, 0xeb9e, 0x9b79, 0x8b58, 0xbb3b, 0xab1a,
    0x6ca6, 0x7c87, 0x4ce4, 0x5cc5, 0x2c22, 0x3c03, 0x0c60, 0x1c41,
    0xedae, 0xfd8f, 0xcdec, 0xddcd, 0xad2a, 0xbd0b, 0x8d68, 0x9d49,
    0x7e97, 0x6eb6, 0x5ed5, 0x4ef4, 0x3e13, 0x2e32, 0x1e51, 0x0e70,
    0xff9f, 0xefbe, 0xdfdd, 0xcffc, 0xbf1b, 0xaf3a, 0x9f59, 0x8f78,
    0x9188, 0x81a9, 0xb1ca, 0xa1eb, 0xd10c, 0xc12d, 0xf14e, 0xe16f,
    0x1080, 0x00a1, 0x30c2, 0x20e3, 0x5004, 0x4025, 0x7046, 0x6067,
    0x83b9, 0x9398, 0xa3fb, 0xb3da, 0xc33d, 0xd31c, 0xe37f, 0xf35e,
    0x02b1, 0x1290, 0x22f3, 0x32d2, 0x4235, 0x5214, 0x6277, 0x7256,
    0xb5ea, 0xa5cb, 0x95a8, 0x8589, 0xf56e, 0xe54f, 0xd52c, 0xc50d,
    0x34e2, 0x24c3, 0x14a0, 0x0481, 0x7466, 0x6447, 0x5424, 0x4405,
    0xa7db, 0xb7fa, 0x8799, 0x97b8, 0xe75f, 0xf77e, 0xc71d, 0xd73c,
    0x26d3, 0x36f2, 0x0691, 0x16b0, 0x6657, 0x7676, 0x4615, 0x5634,
    0xd94c, 0xc96d, 0xf90e, 0xe92f, 0x99c8, 0x89e9, 0xb98a, 0xa9ab,
    0x5844, 0x4865, 0x7806, 0x6827, 0x18c0, 0x08e1, 0x3882, 0x28a3,
    0xcb7d, 0xdb5c, 0xeb3f, 0xfb1e, 0x8bf9, 0x9bd8, 0xabbb, 0xbb9a,
    0x4a75, 0x5a54, 0x6a37, 0x7a16, 0x0af1, 0x1ad0, 0x2ab3, 0x3a92,
    0xfd2e, 0xed0f, 0xdd6c, 0xcd4d, 0xbdaa, 0xad8b, 0x9de8, 0x8dc9,
    0x7c26, 0x6c07, 0x5c64, 0x4c45, 0x3ca2, 0x2c83, 0x1ce0, 0x0cc1,
    0xef1f, 0xff3e, 0xcf5d, 0xdf7c, 0xaf9b, 0xbfba, 0x8fd9, 0x9ff8,
    0x6e17, 0x7e36, 0x4e55, 0x5e74, 0x2e93, 0x3eb2, 0x0ed1, 0x1ef0
};

const uint32 kLogicalVolumeDescriptorBaseSize = sizeof(logical_volume_descriptor)
                                                     - (UDF_MAX_PARTITION_MAPS
                                                        * UDF_MAX_PARTITION_MAP_SIZE);

// entity_ids
entity_id kMetadataPartitionMapId;
entity_id kSparablePartitionMapId;
entity_id kVirtualPartitionMapId;
entity_id kImplementationId;
entity_id kPartitionContentsId1xx;
entity_id kPartitionContentsId2xx;
entity_id kLogicalVolumeInfoId150;
entity_id kLogicalVolumeInfoId201;
entity_id kDomainId150;
entity_id kDomainId201;


//----------------------------------------------------------------------
// Entities initialization
//----------------------------------------------------------------------

void
init_entities()
{
	kMetadataPartitionMapId = entity_id(0, "*UDF Metadata Partition");
	kSparablePartitionMapId = entity_id(0, "*UDF Sparable Partition");
	kVirtualPartitionMapId = entity_id(0, "*UDF Virtual Partition");
	kImplementationId = entity_id(0, "*Haiku UDF", implementation_id_suffix(OS_BEOS, BEOS_GENERIC));
	kPartitionContentsId1xx = entity_id(0, "+NSR02");
	kPartitionContentsId2xx = entity_id(0, "+NSR03");
	kLogicalVolumeInfoId150 = entity_id(0, "*UDF LV Info", udf_id_suffix(0x0150, OS_BEOS, BEOS_GENERIC));
	kLogicalVolumeInfoId201 = entity_id(0, "*UDF LV Info", udf_id_suffix(0x0201, OS_BEOS, BEOS_GENERIC));
	kDomainId150 = entity_id(0, "*OSTA UDF Compliant", domain_id_suffix(0x0150,
                                  DF_HARD_WRITE_PROTECT));
	kDomainId201 = entity_id(0, "*OSTA UDF Compliant", domain_id_suffix(0x0201,
                                  DF_HARD_WRITE_PROTECT));
}


//----------------------------------------------------------------------
// Helper functions
//----------------------------------------------------------------------

const char *tag_id_to_string(tag_id id)
{
	switch (id) {
		case TAGID_UNDEFINED:
			return "undefined";

		case TAGID_PRIMARY_VOLUME_DESCRIPTOR:
			return "primary volume descriptor";			
		case TAGID_ANCHOR_VOLUME_DESCRIPTOR_POINTER:
			return "anchor volume descriptor pointer";
		case TAGID_VOLUME_DESCRIPTOR_POINTER:
			return "volume descriptor pointer";
		case TAGID_IMPLEMENTATION_USE_VOLUME_DESCRIPTOR:
			return "implementation use volume descriptor";
		case TAGID_PARTITION_DESCRIPTOR:
			return "partition descriptor";
		case TAGID_LOGICAL_VOLUME_DESCRIPTOR:
			return "logical volume descriptor";
		case TAGID_UNALLOCATED_SPACE_DESCRIPTOR:
			return "unallocated space descriptor";
		case TAGID_TERMINATING_DESCRIPTOR:
			return "terminating descriptor";
		case TAGID_LOGICAL_VOLUME_INTEGRITY_DESCRIPTOR:
			return "logical volume integrity descriptor";

		case TAGID_FILE_SET_DESCRIPTOR:
			return "file set descriptor";
		case TAGID_FILE_ID_DESCRIPTOR:
			return "file identifier descriptor";
		case TAGID_ALLOCATION_EXTENT_DESCRIPTOR:
			return "allocation extent descriptor";
		case TAGID_INDIRECT_ENTRY:
			return "indirect entry";
		case TAGID_TERMINAL_ENTRY:
			return "terminal entry";
		case TAGID_FILE_ENTRY:
			return "file entry";
		case TAGID_EXTENDED_ATTRIBUTE_HEADER_DESCRIPTOR:
			return "extended attribute header descriptor";
		case TAGID_UNALLOCATED_SPACE_ENTRY:
			return "unallocated space entry";
		case TAGID_SPACE_BITMAP_DESCRIPTOR:
			return "space bitmap descriptor";
		case TAGID_PARTITION_INTEGRITY_ENTRY:
			return "partition integrity entry";
		case TAGID_EXTENDED_FILE_ENTRY:
			return "extended file entry";

		default:
			if (TAGID_CUSTOM_START <= id && id <= TAGID_CUSTOM_END)
				return "custom";
			return "reserved";	
	}
}


//----------------------------------------------------------------------
// volume_structure_descriptor_header
//----------------------------------------------------------------------

volume_structure_descriptor_header::volume_structure_descriptor_header(uint8 type, const char *_id, uint8 version)
	: type(type)
	, version(version)
{
	memcpy(id, _id, 5);
}


/*! \brief Returns true if the given \a id matches the header's id.
*/
bool
volume_structure_descriptor_header::id_matches(const char *id)
{
	return strncmp(this->id, id, 5) == 0;
}


//----------------------------------------------------------------------
// charspec
//----------------------------------------------------------------------

charspec::charspec(uint8 type, const char *info)
{
	set_character_set_type(type);
	set_character_set_info(info);
}

void
charspec::dump() const
{
	DUMP_INIT("charspec");
	PRINT(("character_set_type: %d\n", character_set_type()));
	PRINT(("character_set_info: `%s'\n", character_set_info()));
}

void
charspec::set_character_set_info(const char *info)
{
	memset(_character_set_info, 0, 63);
	if (info)
		strncpy(_character_set_info, info, 63);
}	

//----------------------------------------------------------------------
// timestamp
//----------------------------------------------------------------------

#if _KERNEL_MODE
static
int
get_month_length(int month, int year)
{
	if (0 <= month && month < 12 && year >= 1970) {
		const int monthLengths[12] = { 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31 };
		int result = monthLengths[month];
		if (month == 1 && ((year - 1968) % 4 == 0))
			result++;
		return result;
	} else {
		DEBUG_INIT_ETC(NULL, ("month: %d, year: %d", month, year));
		PRINT(("Invalid month or year! Returning 0\n"));
		return 0;
	}
}
#endif

timestamp::timestamp(time_t time)
{
#if !_KERNEL_MODE
	// Is it me, or is localtime() broken?
	tm *local = localtime(&time);
	if (local) {
		set_microsecond(0);
		set_hundred_microsecond(0);
		set_centisecond(0);
		set_second(local->tm_sec);
		set_minute(local->tm_min);
		set_hour(local->tm_hour);
		set_day(local->tm_mday);
		set_month(local->tm_mon+1);
		set_year(local->tm_year+1900);
		set_type(1);
		set_timezone(local->tm_gmtoff / 60);
	} else {
		_clear();
	}
#else	// no localtime() in the R5 kernel...
	// real_time_clock() is returning the time offset by -16 hours.
	// Considering I'm -8 hours from GMT, this doesn't really make
	// sense. For the moment I'm offsetting it manually here, but
	// I'm not sure what the freaking deal is, and unfortunately,
	// localtime() appears to be broken...
	time += 16 * 60 * 60;

	set_microsecond(0);
	set_hundred_microsecond(0);
	set_centisecond(0);
	set_second(time % 60);
	time = time / 60;	// convert to minutes
	set_minute(time % 60);
	time = time / 60;	// convert to hours
	set_hour(time % 24);
	time = time / 24;	// convert to days
	
	// From here we start at time == 0 and count up
	// by days until we figure out what the day, month,
	// and year are.
	int year = 0;
	int month = 0;
	time_t clock = 0;
	for (clock = 0;
	       clock + get_month_length(month, year+1970) < time; 
	         clock += get_month_length(month, year+1970))
	{		
		month++;
		if (month == 12) {
			year++;
			month = 0;
		}
	}
	int day = time - clock;
	set_day(day);
	set_month(month+1);
	set_year(year+1970);
	set_type(1);
	set_timezone(-2047); // -2047 == no timezone specified
#endif
}

void
timestamp::dump() const
{
	DUMP_INIT("timestamp");
	PRINT(("type:                %d\n", type()));
	PRINT(("timezone:            %d\n", timezone()));
	PRINT(("year:                %d\n", year()));
	PRINT(("month:               %d\n", month()));
	PRINT(("day:                 %d\n", day()));
	PRINT(("hour:                %d\n", hour()));
	PRINT(("minute:              %d\n", minute()));
	PRINT(("second:              %d\n", second()));
	PRINT(("centisecond:         %d\n", centisecond()));
	PRINT(("hundred_microsecond: %d\n", hundred_microsecond()));
	PRINT(("microsecond:         %d\n", microsecond()));
}

void
timestamp::_clear()
{
	set_microsecond(0);
	set_hundred_microsecond(0);
	set_centisecond(0);
	set_second(0);
	set_minute(0);
	set_hour(0);
	set_day(0);
	set_month(0);
	set_year(0);
	set_type(0);
	set_timezone(0);
}

//----------------------------------------------------------------------
// udf_id_suffix
//----------------------------------------------------------------------

udf_id_suffix::udf_id_suffix(uint16 udfRevision, uint8 os_class,
                             uint8 os_identifier)
	: _udf_revision(udfRevision)
	, _os_class(os_class)
	, _os_identifier(os_identifier)
{
	memset(_reserved.data, 0, _reserved.size());
}                                                   

//----------------------------------------------------------------------
// implementation_id_suffix
//----------------------------------------------------------------------

implementation_id_suffix::implementation_id_suffix(uint8 os_class,
                                                   uint8 os_identifier)
	: _os_class(os_class)
	, _os_identifier(os_identifier)
{
	memset(_implementation_use.data, 0, _implementation_use.size());
}                                                   

//----------------------------------------------------------------------
// domain_id_suffix
//----------------------------------------------------------------------

domain_id_suffix::domain_id_suffix(uint16 udfRevision, uint8 domainFlags)
	: _udf_revision(udfRevision)
	, _domain_flags(domainFlags)
{
	memset(_reserved.data, 0, _reserved.size());
}                                                   

//----------------------------------------------------------------------
// entity_id
//----------------------------------------------------------------------

entity_id::entity_id(uint8 flags, const char *identifier, uint8 *identifier_suffix)
	: _flags(flags)
{
	memset(_identifier, 0, kIdentifierLength);
	if (identifier)
		strncpy(_identifier, identifier, kIdentifierLength);
	if (identifier_suffix)
		memcpy(_identifier_suffix.data, identifier_suffix, kIdentifierSuffixLength);
	else
		memset(_identifier_suffix.data, 0, kIdentifierSuffixLength);
}

entity_id::entity_id(uint8 flags, const char *identifier,
	                 const udf_id_suffix &suffix)
	: _flags(flags)
{
	memset(_identifier, 0, kIdentifierLength);
	if (identifier)
		strncpy(_identifier, identifier, kIdentifierLength);
	memcpy(_identifier_suffix.data, &suffix, kIdentifierSuffixLength);
}	                 

entity_id::entity_id(uint8 flags, const char *identifier,
	                 const implementation_id_suffix &suffix)
	: _flags(flags)
{
	memset(_identifier, 0, kIdentifierLength);
	if (identifier)
		strncpy(_identifier, identifier, kIdentifierLength);
	memcpy(_identifier_suffix.data, &suffix, kIdentifierSuffixLength);
}	                 

entity_id::entity_id(uint8 flags, const char *identifier,
	                 const domain_id_suffix &suffix)
	: _flags(flags)
{
	memset(_identifier, 0, kIdentifierLength);
	if (identifier)
		strncpy(_identifier, identifier, kIdentifierLength);
	memcpy(_identifier_suffix.data, &suffix, kIdentifierSuffixLength);
}	                 

void
entity_id::dump() const
{
	DUMP_INIT("entity_id");
	PRINT(("flags:             %d\n", flags()));
	PRINT(("identifier:        `%.23s'\n", identifier()));
	PRINT(("identifier_suffix:\n"));
	DUMP(identifier_suffix());
}

bool
entity_id::matches(const entity_id &id) const
{
	bool result = true;
	for (int i = 0; i < entity_id::kIdentifierLength; i++) {
		if (identifier()[i] != id.identifier()[i]) {
			result = false;
			break;
		}
	}
	return result;
}

//----------------------------------------------------------------------
// extent_address
//----------------------------------------------------------------------

extent_address::extent_address(uint32 location, uint32 length)
{
	set_location(location);
	set_length(length);
}

void
extent_address::dump() const
{
	DUMP_INIT("extent_address");
	PRINT(("length:   %" B_PRIu32 "\n", length()));
	PRINT(("location: %" B_PRIu32 "\n", location()));
}

//----------------------------------------------------------------------
// logical_block_address
//----------------------------------------------------------------------

void
logical_block_address::dump() const
{
	DUMP_INIT("logical_block_address");
	PRINT(("block:     %" B_PRIu32 "\n", block()));
	PRINT(("partition: %d\n", partition()));
}

logical_block_address::logical_block_address(uint16 partition, uint32 block)
{
	set_partition(partition);
	set_block(block);
}

//----------------------------------------------------------------------
// long_address
//----------------------------------------------------------------------

long_address::long_address(uint16 partition, uint32 block, uint32 length,
	                       uint8 type)
{
	set_partition(partition);
	set_block(block);
	set_length(length);
	set_type(type);
	memset(_implementation_use.data, 0, _implementation_use.size());
}
	                       
void
long_address::dump() const
{
	DUMP_INIT("long_address");
	PRINT(("length:   %" B_PRIu32 "\n", length()));
	PRINT(("block:    %" B_PRIu32 "\n", block()));
	PRINT(("partition: %d\n", partition()));
	PRINT(("implementation_use:\n"));
	DUMP(implementation_use());
}

//----------------------------------------------------------------------
// descriptor_tag 
//----------------------------------------------------------------------

void
descriptor_tag::dump() const
{
	DUMP_INIT("descriptor_tag");
	PRINT(("id:            %d (%s)\n", id(), tag_id_to_string(tag_id(id()))));
	PRINT(("version:       %d\n", version()));
	PRINT(("checksum:      %d\n", checksum()));
	PRINT(("serial_number: %d\n", serial_number()));
	PRINT(("crc:           %d\n", crc()));
	PRINT(("crc_length:    %d\n", crc_length()));
	PRINT(("location:      %" B_PRIu32 "\n", location()));
}


/*! \brief Calculates the tag's CRC, verifies the tag's checksum, and
	verifies the tag's location on the medium.
	
	Note that this function makes the assumption that the descriptor_tag
	is the first data member in a larger descriptor structure, the remainder
	of which immediately follows the descriptor_tag itself in memory. This
	is generally a safe assumption, as long as the entire descriptor (and
	not the its tag) is read in before init_check() is called. If this is
	not the case, it's best to call this function with a \a calculateCrc
	value of false, to keep from trying to calculate a crc value on invalid
	and possibly unowned memory.
	
	\param block The block location of this descriptor as taken from the
	             corresponding allocation descriptor. If the address specifies
	             a block in a partition, the partition block is the desired
	             location, not the mapped physical disk block.
	\param calculateCrc Whether or not to perform the crc calculation
	                    on the descriptor data following the tag.                
*/
status_t 
descriptor_tag::init_check(uint32 block, bool calculateCrc)
{
	DEBUG_INIT_ETC("descriptor_tag", ("location: %" B_PRIu32 ", "
			"calculateCrc: %s",
		block, bool_to_string(calculateCrc)));
	PRINT(("location   (parameter)    == %" B_PRIu32 "\n", block));
	PRINT(("location   (in structure) == %" B_PRIu32 "\n", location()));
	if (calculateCrc) {
		PRINT(("crc        (calculated)   == %d\n",
		       calculate_crc(reinterpret_cast<uint8*>(this)+sizeof(descriptor_tag),
		       crc_length())))
	} else {
		PRINT(("crc        (calculated)   == (not calculated)\n"));
	}
	PRINT(("crc        (in structure) == %d\n", crc()));
	PRINT(("crc_length (in structure) == %d\n", crc_length()));
	// location
	status_t error = (block == location()) ? B_OK : B_NO_INIT;
	// checksum
	if (!error) {
		uint32 sum = 0;
		for (int i = 0; i <= 3; i++)
			sum += reinterpret_cast<uint8*>(this)[i];
		for (int i = 5; i <= 15; i++)
			sum += reinterpret_cast<uint8*>(this)[i];
		error = sum % 256 == checksum() ? B_OK : B_NO_INIT;
	}
	// crc
	if (!error && calculateCrc) {
		uint16 _crc = calculate_crc(reinterpret_cast<uint8*>(this)
		               + sizeof(descriptor_tag), crc_length());
		error = _crc == crc() ? B_OK : B_NO_INIT;
	}	
	RETURN(error);	
}

//----------------------------------------------------------------------
// primary_volume_descriptor
//----------------------------------------------------------------------

void
primary_volume_descriptor::dump() const
{
	DUMP_INIT("primary_volume_descriptor");
	
	UdfString string;
	
	PRINT(("tag:\n"));
	DUMP(tag());
	PRINT(("vds_number:                       %" B_PRIu32 "\n", vds_number()));
	PRINT(("primary_volume_descriptor_number: %" B_PRIu32 "\n",
		primary_volume_descriptor_number()));
	string = volume_identifier();
	PRINT(("volume_identifier:                `%s'\n", string.Utf8()));
	PRINT(("volume_sequence_number:           %d\n", volume_sequence_number()));
	PRINT(("max_volume_sequence_number:       %d\n", max_volume_sequence_number()));
	PRINT(("interchange_level:                %d\n", interchange_level()));
	PRINT(("max_interchange_level:            %d\n", max_interchange_level()));
	PRINT(("character_set_list:               %" B_PRIu32 "\n",
		character_set_list()));
	PRINT(("max_character_set_list:           %" B_PRIu32 "\n",
		max_character_set_list()));
	string = volume_set_identifier();
	PRINT(("volume_set_identifier:            `%s'\n", string.Utf8()));
	PRINT(("descriptor_character_set:\n"));
	DUMP(descriptor_character_set());
	PRINT(("explanatory_character_set:\n"));
	DUMP(explanatory_character_set());
	PRINT(("volume_abstract:\n"));
	DUMP(volume_abstract());
	PRINT(("volume_copyright_notice:\n"));
	DUMP(volume_copyright_notice());
	PRINT(("application_id:\n"));
	DUMP(application_id());
	PRINT(("recording_date_and_time:\n"));
	DUMP(recording_date_and_time());
	PRINT(("implementation_id:\n"));
	DUMP(implementation_id());
	PRINT(("implementation_use:\n"));
	DUMP(implementation_use());
	PRINT(("predecessor_vds_location:         %" B_PRIu32 "\n",
		predecessor_volume_descriptor_sequence_location()));
	PRINT(("flags:                            %d\n", flags()));       
}


//----------------------------------------------------------------------
// anchor_volume_descriptor_pointer
//----------------------------------------------------------------------

void
anchor_volume_descriptor::dump() const
{
	DUMP_INIT("anchor_volume_descriptor");
	PRINT(("tag:\n"));
	DUMP(tag());
	PRINT(("main_vds:\n"));
	DUMP(main_vds());
	PRINT(("reserve_vds:\n"));
	DUMP(reserve_vds());
}

//----------------------------------------------------------------------
// logical_volume_info
//----------------------------------------------------------------------

void
logical_volume_info::dump() const
{
	UdfString string;
	DUMP_INIT("logical_volume_information");
	PRINT(("character_set:\n"));
	DUMP(character_set());
	string = logical_volume_id();
	PRINT(("logical_volume_id: `%s'\n", string.Utf8()));
	for (uint32 i = 0; i < _logical_volume_info.length(); i++) {
		string = _logical_volume_info[i];
		PRINT(("logical_volume_info #%" B_PRIu32 ": %s\n", i, string.Utf8()));
	}
	PRINT(("implementation_id:\n"));
	DUMP(implementation_id());
	PRINT(("implementation_use:\n"));
	DUMP(implementation_use());
}

//----------------------------------------------------------------------
// implementation_use_descriptor
//----------------------------------------------------------------------

void
implementation_use_descriptor::dump() const
{
	DUMP_INIT("implementation_use_descriptor");
	PRINT(("tag:\n"));
	DUMP(tag());
	PRINT(("vds_number: %" B_PRIu32 "\n", vds_number()));
	PRINT(("implementation_id:\n"));
	DUMP(implementation_id());
	PRINT(("implementation_use: XXX\n"));
	DUMP(implementation_use());
}

//----------------------------------------------------------------------
// partition_descriptor
//----------------------------------------------------------------------

void
partition_descriptor::dump() const
{
	DUMP_INIT("partition_descriptor");
	PRINT(("tag:\n"));
	DUMP(tag());
	PRINT(("vds_number:                %" B_PRIu32 "\n", vds_number()));
	PRINT(("partition_flags:           %d\n", partition_flags()));
	PRINT(("partition_flags.allocated: %s\n", allocated() ? "true" : "false"));
	PRINT(("partition_number:          %d\n", partition_number()));
	PRINT(("partition_contents:\n"));
	DUMP(partition_contents());
	PRINT(("partition_contents_use:    XXX\n"));
	DUMP(partition_contents_use());
	PRINT(("access_type:               %" B_PRIu32 "\n", access_type()));
	PRINT(("start:                     %" B_PRIu32 "\n", start()));
	PRINT(("length:                    %" B_PRIu32 "\n", length()));
	PRINT(("implementation_id:\n"));
	DUMP(implementation_id());
	PRINT(("implementation_use:        XXX\n"));
	DUMP(implementation_use());
}

//----------------------------------------------------------------------
// logical_volume_descriptor
//----------------------------------------------------------------------

void
logical_volume_descriptor::dump() const
{
	DUMP_INIT("logical_volume_descriptor");
	PRINT(("tag:\n"));
	DUMP(tag());
	PRINT(("vds_number:                %" B_PRIu32 "\n", vds_number()));
	PRINT(("character_set:\n"));
	DUMP(character_set());
	UdfString string(logical_volume_identifier());
	PRINT(("logical_volume_identifier: `%s'\n", string.Utf8()));
	PRINT(("logical_block_size:        %" B_PRIu32 "\n",
		logical_block_size()));
	PRINT(("domain_id:\n"));
	DUMP(domain_id());
	PRINT(("logical_volume_contents_use:\n"));
	DUMP(logical_volume_contents_use());
	PRINT(("file_set_address:\n"));
	DUMP(file_set_address());
	PRINT(("map_table_length:          %" B_PRIu32 "\n", map_table_length()));
	PRINT(("partition_map_count:       %" B_PRIu32 "\n",
		partition_map_count()));
	PRINT(("implementation_id:\n"));
	DUMP(implementation_id());
	PRINT(("implementation_use:\n"));
	DUMP(implementation_use());
	PRINT(("integrity_sequence_extent:\n"));
	DUMP(integrity_sequence_extent());
//	PRINT(("partition_maps:\n"));
	const uint8 *maps = partition_maps();
	int offset = 0;
	for (uint i = 0; i < partition_map_count(); i++) {
		PRINT(("partition_map #%d:\n", i));
		uint8 type = maps[offset];
		uint8 length = maps[offset+1];
		PRINT(("  type: %d\n", type));
		PRINT(("  length: %d\n", length));
		switch (type) {
			case 1:
				for (int j = 0; j < length-2; j++) 
					PRINT(("  data[%d]: %d\n", j, maps[offset+2+j]));
				break;
			case 2: {
				PRINT(("  partition_number: %d\n", *reinterpret_cast<const uint16*>(&(maps[offset+38]))));
				PRINT(("  entity_id:\n"));
				const entity_id *id = reinterpret_cast<const entity_id*>(&(maps[offset+4]));
				if (id)	// To kill warning when DEBUG==0
					PDUMP(id);
				break;
			}
		}
		offset += maps[offset+1];
	}
	// \todo dump partition_maps
}


logical_volume_descriptor&
logical_volume_descriptor::operator=(const logical_volume_descriptor &rhs)
{
	_tag = rhs._tag;
	_vds_number = rhs._vds_number;
	_character_set = rhs._character_set;
	_logical_volume_identifier = rhs._logical_volume_identifier;
	_logical_block_size = rhs._logical_block_size;
	_domain_id = rhs._domain_id;
	_logical_volume_contents_use = rhs._logical_volume_contents_use;
	_map_table_length = rhs._map_table_length;
	_partition_map_count = rhs._partition_map_count;
	_implementation_id = rhs._implementation_id;
	_implementation_use = rhs._implementation_use;
	_integrity_sequence_extent = rhs._integrity_sequence_extent;
	// copy the partition maps one by one
	uint8 *lhsMaps = partition_maps();
	const uint8 *rhsMaps = rhs.partition_maps();
	int offset = 0;
	for (uint8 i = 0; i < rhs.partition_map_count(); i++) {
		uint8 length = rhsMaps[offset+1];
		memcpy(&lhsMaps[offset], &rhsMaps[offset], length);
		offset += length;		
	}
	return *this;
}


//----------------------------------------------------------------------
// physical_partition_map 
//----------------------------------------------------------------------

void
physical_partition_map::dump()
{
	DUMP_INIT("physical_partition_map");
	PRINT(("type: %d\n", type()));
	PRINT(("length: %d\n", length()));
	PRINT(("volume_sequence_number: %d\n", volume_sequence_number()));
	PRINT(("partition_number: %d\n", partition_number()));
}

//----------------------------------------------------------------------
// sparable_partition_map 
//----------------------------------------------------------------------

void
sparable_partition_map::dump()
{
	DUMP_INIT("sparable_partition_map");
	PRINT(("type: %d\n", type()));
	PRINT(("length: %d\n", length()));
	PRINT(("partition_type_id:"));
	DUMP(partition_type_id());
	PRINT(("volume_sequence_number: %d\n", volume_sequence_number()));
	PRINT(("partition_number: %d\n", partition_number()));
	PRINT(("sparing_table_count: %d\n", sparing_table_count()));
	PRINT(("sparing_table_size: %" B_PRIu32 "\n", sparing_table_size()));
	PRINT(("sparing_table_locations:"));
	for (uint8 i = 0; i < sparing_table_count(); i++)
		PRINT(("  %d: %" B_PRIu32 "\n", i, sparing_table_location(i)));
}

//----------------------------------------------------------------------
// unallocated_space_descriptor
//----------------------------------------------------------------------

void
unallocated_space_descriptor::dump() const
{
	DUMP_INIT("unallocated_space_descriptor");
	PRINT(("tag:\n"));
	DUMP(tag());
	PRINT(("vds_number:                  %" B_PRIu32 "\n", vds_number()));
	PRINT(("allocation_descriptor_count: %" B_PRIu32 "\n",
		allocation_descriptor_count()));
	// \todo dump alloc_descriptors
}


//----------------------------------------------------------------------
// terminating_descriptor
//----------------------------------------------------------------------

void
terminating_descriptor::dump() const
{
	DUMP_INIT("terminating_descriptor");
	PRINT(("tag:\n"));
	DUMP(tag());
}

//----------------------------------------------------------------------
// file_set_descriptor
//----------------------------------------------------------------------

void
file_set_descriptor::dump() const
{
	DUMP_INIT("file_set_descriptor");
	PRINT(("tag:\n"));
	DUMP(tag());
	PRINT(("recording_date_and_time:\n"));
	DUMP(recording_date_and_time());
	PRINT(("interchange_level: %d\n", interchange_level()));
	PRINT(("max_interchange_level: %d\n", max_interchange_level()));
	PRINT(("character_set_list: %" B_PRIu32 "\n", character_set_list()));
	PRINT(("max_character_set_list: %" B_PRIu32 "\n",
		max_character_set_list()));
	PRINT(("file_set_number: %" B_PRIu32 "\n", file_set_number()));
	PRINT(("file_set_descriptor_number: %" B_PRIu32 "\n",
		file_set_descriptor_number()));
	PRINT(("logical_volume_id_character_set:\n"));
	DUMP(logical_volume_id_character_set());
	PRINT(("logical_volume_id:\n"));
	DUMP(logical_volume_id());
	PRINT(("file_set_id_character_set:\n"));
	DUMP(file_set_id_character_set());
	PRINT(("file_set_id:\n"));
	DUMP(file_set_id());
	PRINT(("copyright_file_id:\n"));
	DUMP(copyright_file_id());
	PRINT(("abstract_file_id:\n"));
	DUMP(abstract_file_id());
	PRINT(("root_directory_icb:\n"));
	DUMP(root_directory_icb());
	PRINT(("domain_id:\n"));
	DUMP(domain_id());
	PRINT(("next_extent:\n"));
	DUMP(next_extent());
	PRINT(("system_stream_directory_icb:\n"));
	DUMP(system_stream_directory_icb());
}

//----------------------------------------------------------------------
// logical_volume_integrity_descriptor
//----------------------------------------------------------------------

void
logical_volume_integrity_descriptor::dump() const
{
	DUMP_INIT("logical_volume_integrity_descriptor");
	PRINT(("tag:\n"));
	DUMP(tag());
	PRINT(("recording_time:\n"));
	DUMP(recording_time());
	PRINT(("integrity_type:             "));
	switch (integrity_type()) {
		case INTEGRITY_OPEN:
			SIMPLE_PRINT(("open\n"));
			break;
		case INTEGRITY_CLOSED:
			SIMPLE_PRINT(("closed\n"));
			break;
		default:
			SIMPLE_PRINT(("invalid integrity type (%" B_PRIu32 ")",
				integrity_type()));
			break;
	}
	PRINT(("next_integrity_extent:\n"));
	DUMP(next_integrity_extent());
	PRINT(("logical_volume_contents_use:\n"));
	DUMP(logical_volume_contents_use());
	PRINT(("next_unique_id:             %" B_PRIu64 "\n", next_unique_id()));
	PRINT(("partition_count:            %" B_PRIu32 "\n", partition_count()));
	PRINT(("implementation_use_length:  %" B_PRIu32 "\n",
		implementation_use_length()));
	if (partition_count() > 0) {
		PRINT(("free_space_table:\n"));
		for (uint32 i = 0; i < partition_count(); i++) {
			PRINT(("partition %" B_PRIu32 ": %" B_PRIu32 " free blocks\n",
				i, free_space_table()[i]));
		}
		PRINT(("size_table:\n"));
		for (uint32 i = 0; i < partition_count(); i++) {
			PRINT(("partition %" B_PRIu32 ": %" B_PRIu32 " blocks large\n",
				i, size_table()[i]));
		}
	}
		
	if (implementation_use_length() >= minimum_implementation_use_length) {
		PRINT(("implementation_id:\n"));
		DUMP(implementation_id());
		PRINT(("file_count:                 %" B_PRIu32 "\n", file_count()));
		PRINT(("directory_count:            %" B_PRIu32 "\n",
			directory_count()));
		PRINT(("minimum_udf_read_revision:  0x%04x\n", minimum_udf_read_revision()));
		PRINT(("minimum_udf_write_revision: 0x%04x\n", minimum_udf_write_revision()));
		PRINT(("maximum_udf_write_revision: 0x%04x\n", maximum_udf_write_revision()));	
	} else {
		PRINT(("NOTE: implementation_use() field of insufficient length to contain \n"));
		PRINT(("      appropriate UDF-2.50 2.2.6.4 fields.\n"));
	}	
}

//----------------------------------------------------------------------
// file_id_descriptor
//----------------------------------------------------------------------

void
file_id_descriptor::dump() const
{
	DUMP_INIT("file_id_descriptor");
	PRINT(("tag:\n"));
	DUMP(tag());
	PRINT(("version_number:            %d\n", version_number()));
	PRINT(("may_be_hidden:             %d\n", may_be_hidden()));
	PRINT(("is_directory:              %d\n", is_directory()));
	PRINT(("is_deleted:                %d\n", is_deleted()));
	PRINT(("is_parent:                 %d\n", is_parent()));
	PRINT(("is_metadata_stream:        %d\n", is_metadata_stream()));
	PRINT(("id_length:                 %d\n", id_length()));
	PRINT(("icb:\n"));
	DUMP(icb());
	PRINT(("implementation_use_length: %d\n", is_parent()));
	UdfString fileId(id());
	PRINT(("id: `%s'", fileId.Utf8()));
}

//----------------------------------------------------------------------
// icb_entry_tag
//----------------------------------------------------------------------

void
icb_entry_tag::dump() const
{
	DUMP_INIT("icb_entry_tag");
	PRINT(("prior_entries: %" B_PRIu32 "\n",
		prior_recorded_number_of_direct_entries()));
	PRINT(("strategy_type: %d\n", strategy_type()));
	PRINT(("strategy_parameters:\n"));
	DUMP(strategy_parameters());
	PRINT(("entry_count: %d\n", entry_count()));
	PRINT(("file_type: %d\n", file_type()));
	PRINT(("parent_icb_location:\n"));
	DUMP(parent_icb_location());
	PRINT(("all_flags: %d\n", flags()));
	
/*
	uint32 prior_recorded_number_of_direct_entries;
	uint16 strategy_type;
	array<uint8, 2> strategy_parameters;
	uint16 entry_count;
	uint8 reserved;
	uint8 file_type;
	logical_block_address parent_icb_location;
	union {
		uint16 all_flags;
		struct {
			uint16	descriptor_flags:3,			
					if_directory_then_sort:1,	//!< To be set to 0 per UDF-2.01 2.3.5.4
					non_relocatable:1,
					archive:1,
					setuid:1,
					setgid:1,
					sticky:1,
					contiguous:1,
					system:1,
					transformed:1,
					multi_version:1,			//!< To be set to 0 per UDF-2.01 2.3.5.4
					is_stream:1,
					reserved_icb_entry_flags:2;
		} flags;
	};

*/

}

//----------------------------------------------------------------------
// icb_header
//----------------------------------------------------------------------

void
icb_header::dump() const
{
	DUMP_INIT("icb_header");

	PRINT(("tag:\n"));
	DUMP(tag());
	PRINT(("icb_tag:\n"));
	DUMP(icb_tag());
	
}

//----------------------------------------------------------------------
// file_icb_entry
//----------------------------------------------------------------------

long_address file_icb_entry::_dummy_stream_directory_icb;

void
file_icb_entry::dump() const
{
	DUMP_INIT("file_icb_entry");

	PRINT(("tag:\n"));
	DUMP(tag());
	PRINT(("icb_tag:\n"));
	DUMP(icb_tag());
	
	PRINT(("uid:                       %" B_PRIu32 ", 0x%" B_PRIx32 "\n",
		uid(), uid()));
	PRINT(("gid:                       %" B_PRIu32 ", 0x%" B_PRIx32 "\n",
		gid(), gid()));
	PRINT(("permissions:               %" B_PRIu32 ", 0x%" B_PRIx32 "\n",
		permissions(), permissions()));
	PRINT(("file_link_count:           %d\n", file_link_count()));
	PRINT(("record_format:             %d\n", record_format()));
	PRINT(("record_display_attributes: %d\n", record_display_attributes()));
	PRINT(("record_length:             %d\n", record_length()));
	PRINT(("information_length:        %" B_PRIu64 "\n",
		information_length()));
	PRINT(("logical_blocks_recorded:   %" B_PRIu64 "\n",
		logical_blocks_recorded()));
	PRINT(("access_date_and_time:\n"));
	DUMP(access_date_and_time());
	PRINT(("modification_date_and_time:\n"));
	DUMP(modification_date_and_time());
	PRINT(("attribute_date_and_time:\n"));
	DUMP(attribute_date_and_time());
	PRINT(("checkpoint:                %" B_PRIu32 "\n", checkpoint()));
	
	PRINT(("extended_attribute_icb:\n"));
	DUMP(extended_attribute_icb());
	PRINT(("implementation_id:\n"));
	DUMP(implementation_id());

	PRINT(("unique_id: %" B_PRIu64 "\n", unique_id()));
	PRINT(("extended_attributes_length:    %" B_PRIu32 "\n",
		extended_attributes_length()));
	PRINT(("allocation_descriptors_length: %" B_PRIu32 "\n",
		allocation_descriptors_length()));

	PRINT(("allocation_descriptors:\n"));
	switch (icb_tag().descriptor_flags()) {
		case ICB_DESCRIPTOR_TYPE_SHORT:
			PRINT(("  short descriptors...\n"));
			break;
		case ICB_DESCRIPTOR_TYPE_LONG:
		{
			const long_address *address = reinterpret_cast<const long_address*>(allocation_descriptors());
			for (uint32 length = allocation_descriptors_length();
				   length >= sizeof(long_address);
				     length -= sizeof(long_address), address++)
			{
				PDUMP(address);
			}		
			break;
		}
		case ICB_DESCRIPTOR_TYPE_EXTENDED:
			PRINT(("  extended descriptors...\n"));
			break;
		case ICB_DESCRIPTOR_TYPE_EMBEDDED:
			PRINT(("  embedded descriptors...\n"));
			break;
		default:
			PRINT(("  invalid descriptors type\n"));
			break;
	}
}

//----------------------------------------------------------------------
// extended_file_icb_entry
//----------------------------------------------------------------------

void
extended_file_icb_entry::dump() const
{
	DUMP_INIT("extended_file_icb_entry");

	PRINT(("tag:\n"));
	DUMP(tag());
	PRINT(("icb_tag:\n"));
	DUMP(icb_tag());
	
	PRINT(("uid:                       %" B_PRIu32 ", 0x%" B_PRIx32 "\n",
		uid(), uid()));
	PRINT(("gid:                       %" B_PRIu32 ", 0x%" B_PRIx32 "\n",
		gid(), gid()));
	PRINT(("permissions:               %" B_PRIu32 ", 0x%" B_PRIx32 "\n",
		permissions(), permissions()));
	PRINT(("file_link_count:           %d\n", file_link_count()));
	PRINT(("record_format:             %d\n", record_format()));
	PRINT(("record_display_attributes: %d\n", record_display_attributes()));
	PRINT(("record_length:             %" B_PRIu32 "\n", record_length()));
	PRINT(("information_length:        %" B_PRIu64 "\n",
		information_length()));
	PRINT(("logical_blocks_recorded:   %" B_PRIu64 "\n",
		logical_blocks_recorded()));
	PRINT(("access_date_and_time:\n"));
	DUMP(access_date_and_time());
	PRINT(("modification_date_and_time:\n"));
	DUMP(modification_date_and_time());
	PRINT(("creation_date_and_time:\n"));
	DUMP(creation_date_and_time());
	PRINT(("attribute_date_and_time:\n"));
	DUMP(attribute_date_and_time());
	PRINT(("checkpoint:                %" B_PRIu32 "\n", checkpoint()));
	
	PRINT(("extended_attribute_icb:\n"));
	DUMP(extended_attribute_icb());
	PRINT(("stream_directory_icb:\n"));
	DUMP(stream_directory_icb());
	PRINT(("implementation_id:\n"));
	DUMP(implementation_id());

	PRINT(("unique_id: %" B_PRIu64 "\n", unique_id()));
	PRINT(("extended_attributes_length:    %" B_PRIu32 "\n",
		extended_attributes_length()));
	PRINT(("allocation_descriptors_length: %" B_PRIu32 "\n",
		allocation_descriptors_length()));
	
	PRINT(("allocation_descriptors:\n"));
	switch (icb_tag().descriptor_flags()) {
		case ICB_DESCRIPTOR_TYPE_SHORT:
			PRINT(("  short descriptors...\n"));
			break;
		case ICB_DESCRIPTOR_TYPE_LONG:
		{
			const long_address *address = reinterpret_cast<const long_address*>(allocation_descriptors());
			for (uint32 length = allocation_descriptors_length();
				   length >= sizeof(long_address);
				     length -= sizeof(long_address), address++)
			{
				PDUMP(address);
			}		
			break;
		}
		case ICB_DESCRIPTOR_TYPE_EXTENDED:
			PRINT(("  extended descriptors...\n"));
			break;
		case ICB_DESCRIPTOR_TYPE_EMBEDDED:
			PRINT(("  embedded descriptors...\n"));
			break;
		default:
			PRINT(("  invalid descriptors type\n"));
			break;
	}
}
